//
//  QFilterMethod.m
//  ImageEffect
//
//  Created by lichq on 13-12-24.
//  Copyright (c) 2013年 lichq. All rights reserved.
//

#import "QFilterMethod.h"
#import "ColorMatrix.h"

@implementation QFilterMethod

unsigned char roundCGFloatToUChar( CGFloat p ) {
    if ( p < 0 ) return 0;
    if ( p > 255 ) return 255;
    return p;
}

unsigned char roundIntToUChar( int p ) {
    if ( p < 0 ) return 0;
    if ( p > 255 ) return 255;
    return p;
}


//////////////////////////////         Change RGBA        ////////////////////////////////////////
#pragma mark - Change RGBA
void changeRGBA_method1(int *red,int *green,int *blue,int *alpha, const float* f)
{
    int redV = *red;
    int greenV = *green;
    int blueV = *blue;
    int alphaV = *alpha;
    
    *red = f[0] * redV + f[1] * greenV + f[2] * blueV + f[3] * alphaV + f[4];
    *green = f[0+5] * redV + f[1+5] * greenV + f[2+5] * blueV + f[3+5] * alphaV + f[4+5];
    *blue = f[0+5*2] * redV + f[1+5*2] * greenV + f[2+5*2] * blueV + f[3+5*2] * alphaV + f[4+5*2];
    *alpha = f[0+5*3] * redV + f[1+5*3] * greenV + f[2+5*3] * blueV + f[3+5*3] * alphaV + f[4+5*3];
    
    *red = roundIntToUChar(*red);
    *green = roundIntToUChar(*green);
    *blue = roundIntToUChar(*blue);
    *alpha = roundIntToUChar(*alpha);
}


void changeRGBA_lomoMethod1(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_lomo);
}

void changeRGBA_heibai(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_heibai);
}

void changeRGBA_huaijiu(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_huaijiu);
}

void changeRGBA_gete(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_gete);
}

void changeRGBA_ruise(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_ruise);
}



void changeRGBA_danya(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_danya);
}

void changeRGBA_jiuhong(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_jiuhong);
}

void changeRGBA_qingning(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_qingning);
}

void changeRGBA_langman(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_langman);
}

void changeRGBA_guangyun(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_guangyun);
}


void changeRGBA_landiao(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_landiao);
}

void changeRGBA_menghuan(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_menghuan);
}

void changeRGBA_yese(int *red,int *green,int *blue,int *alpha){
    changeRGBA_method1(red, green, blue, alpha, colormatrix_yese);
}
////////////////////////////////////////////////////////////////////////////////////////////////////


void changeRGBA_lomoNTSC(int *pRed,int *pGreen,int *pBlue,int *pAlpha){
    int red = *pRed;
    int green = *pGreen;
    int blue = *pBlue;
    int alpha = *pAlpha;
    
    // NTSC系加重平均法
    int gray  = 0.298912 * red + 0.586611 * green + 0.114478 * blue;
    
    *pRed   = gray + 20.01;
    *pGreen = gray -  2.46;
    *pBlue  = gray - 41.28;
    *pAlpha = alpha;
    
    *pRed = roundIntToUChar(*pRed);
    *pGreen = roundIntToUChar(*pGreen);
    *pBlue = roundIntToUChar(*pBlue);
    *pAlpha = roundIntToUChar(*pAlpha);
}











//////////////////////////////////     Change Pixel     /////////////////////////////////////
#pragma mark - Change Pixel
void changePixelFun(unsigned char **imagePixel, int w, int h, void (*changeRGBAFun)(int *red,int *green,int *blue,int *alpha) ){
    
    unsigned char *imgPixel = *imagePixel;
    int red,green,blue,alpha;
    long startByte = 0;
    
    for(int y = 0;y< h;y++)//双层循环按照长宽的像素个数迭代每个像素点
	{
		for (int x = 0; x<w; x++)
		{
            startByte = (y * w + x) * 4;
            
			red = imgPixel[startByte];
			green = imgPixel[startByte+1];
			blue = imgPixel[startByte+2];
            alpha = imgPixel[startByte+3];
            
            changeRGBAFun(&red, &green, &blue, &alpha);
            
            //回写数据
			imgPixel[startByte] = red;
			imgPixel[startByte+1] = green;
			imgPixel[startByte+2] = blue;
            imgPixel[startByte+3] = alpha;
		}
	}
    
    imagePixel = &imgPixel;
}



void changePixel_lomoNTSC(unsigned char **imagePixel, int w, int h){

    changePixelFun(imagePixel, w, h, changeRGBA_lomoNTSC);
    
    /*
    unsigned char *imgPixel = *imagePixel;
    int red,green,blue,alpha;
    long startByte = 0;
    for(int y = 0;y< h;y++)
	{
		for (int x = 0; x<w; x++)
		{
            startByte = (y * w + x) * 4;
            
			red = imgPixel[startByte];
			green = imgPixel[startByte+1];
			blue = imgPixel[startByte+2];
            alpha = imgPixel[startByte+3];
            
            changeRGBA_lomoNTSC(&red, &green, &blue, &alpha);
            
			imgPixel[startByte] = red;
			imgPixel[startByte+1] = green;
			imgPixel[startByte+2] = blue;
            imgPixel[startByte+3] = alpha;
		}
	}
    
    imagePixel = &imgPixel;
    */
}

void changePixel_lomoMethod1(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_lomoMethod1);
}

void changePixel_heibai(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_heibai);
}

void changePixel_huaijiu(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_huaijiu);
}

void changePixel_gete(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_gete);
}

void changePixel_ruise(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_ruise);
}



void changePixel_danya(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_danya);
}

void changePixel_jiuhong(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_jiuhong);
}

void changePixel_qingning(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_qingning);
}

void changePixel_langman(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_langman);
}

void changePixel_guangyun(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_guangyun);
}



void changePixel_landiao(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_landiao);
}

void changePixel_menghuan(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_menghuan);
}
    
void changePixel_yese(unsigned char **imagePixel, int w, int h){
    changePixelFun(imagePixel, w, h, changeRGBA_yese);
}


void changePixel_LomoMethod3(unsigned char **imagePixel, int w, int h){
    unsigned char *imgPixel = *imagePixel;
    unsigned char red,green,blue,alpha;
    long startByte = 0;
    
    //    changePixelFun(imagePixel, w, h, changeRGBA_);
    
    float redTemp,greenTemp,blueTemp,alphaTemp;
    
    
    for(int y = 0;y< h;y++)
	{
		for (int x = 0; x<w; x++)
		{
            startByte = (y * w + x) * 4;
            
			red = imgPixel[startByte];
			green = imgPixel[startByte+1];
			blue = imgPixel[startByte+2];
            alpha = imgPixel[startByte+3];
            
            int  value1 = red < 128 ? red : 256 -red;
            redTemp = (value1 * value1 * value1)/64/256;
            redTemp = red < 128 ? redTemp : (256 - redTemp);
            
            int value2 = green < 128 ? green : 256 - green;
            greenTemp = (value2 * value2)/128;
            greenTemp = green < 128 ? greenTemp : (256 - greenTemp);
            
            blueTemp = blue/2 + 0x25;
            
            alphaTemp = alpha;
            
            int ratio = w > h ? 32768*h/w : 32768*w/h;
            int cx = w >> 1;
            int cy = h >> 1;
            int max = cx * cx + cy * cy;
            int min = (int)(max * (1 - 0.8f));
            int diff = max - min;
            
            //边缘黑暗
            int dx = cx - x;
            int dy = cy - y;
            if (w > h) {
                dx = (dx *ratio) >> 15;
            }else{
                dy = (dy * ratio) >> 15;
            }
            int distSq = dx * dx + dy *dy;
            if (distSq > min) {
                int v = ( (max - distSq) << 8 )/diff;
                v *= v;
                
                int ri = (int)(redTemp *v) >> 16;
                int gi = (int)(greenTemp *v) >> 16;
                int bi = (int)(blueTemp * v) >> 16;
                
                redTemp = ri > 255 ? 255 : (ri < 0 ? 0 : ri);
                greenTemp  = gi > 255 ? 255 : (gi < 0 ? 0 : gi);
                blueTemp = bi > 255 ? 255 : (bi < 0 ? 0 :bi);
            }
            
            
            red = roundIntToUChar(redTemp);
            green = roundIntToUChar(greenTemp);
            blue = roundIntToUChar(blueTemp);
            alpha = roundIntToUChar(alphaTemp);
            
			imgPixel[startByte] = red;
			imgPixel[startByte+1] = green;
			imgPixel[startByte+2] = blue;
            imgPixel[startByte+3] = alpha;
		}
	}
}

void changePixel_Sketch(unsigned char **imagePixel, int w, int h){
    unsigned char *imgPixel = *imagePixel;
    unsigned char red,green,blue,alpha;
    long startByte = 0;
    
//    changePixelFun(imagePixel, w, h, changeRGBA_);
    
    unsigned char inverse[1000*1000] = {0};
    unsigned char gray[1000*1000] = {0};
    long pixelIndex = 0;
    
    for(int y = 0;y< h;y++)
	{
		for (int x = 0; x<w; x++)
		{
            pixelIndex = y * w + x;
            startByte = (y * w + x) * 4;
            
			red = imgPixel[startByte];
			green = imgPixel[startByte+1];
			blue = imgPixel[startByte+2];
            alpha = imgPixel[startByte+3];
            
            gray[pixelIndex] = red/3 + green/3 + blue/3;
            
            inverse[pixelIndex] = 255 - gray[y*w+x];
        }
    }
    
    
    int  sum;
    unsigned char guass_blur[1000*1000] = {0};
    
    int a = 0,b = 0, temp = 0;
    float ex;
    
    for(int y = 0;y< h;y++)
	{
		for (int x = 0; x<w; x++)
		{
            pixelIndex = (y * w + x);
            
            if ( (y==0) || (y == h-1) || (x==0) || (x==w-1) ){
                guass_blur[pixelIndex] = 0;
            }else{
                sum = 1*inverse[pixelIndex - w - 1] + 2*inverse[pixelIndex - w] + 1*inverse[pixelIndex - w + 1]
                +2*inverse[pixelIndex - 1]     + 4*inverse[pixelIndex]     + 2*inverse[pixelIndex + 1]
                +1*inverse[pixelIndex + w - 1] + 2*inverse[pixelIndex + w] + 1*inverse[pixelIndex + w + 1];
                
                guass_blur[pixelIndex] = sum/16;
            }
            
            b = guass_blur[pixelIndex];
            a= gray[pixelIndex];;
            temp = a + a*b/(256-b);
            
            ex = temp *temp * 1.0 /255/255;
            temp = temp * ex;
            
            a = MIN(temp, 255);
            
            red = a;
            green = a;
            blue =a;
            
            startByte = pixelIndex * 4;

			imgPixel[startByte] = red;
			imgPixel[startByte+1] = green;
			imgPixel[startByte+2] = blue;
            imgPixel[startByte+3] = alpha;
        }
    }
}


void changePixel_Scrap(unsigned char **imagePixel, int w, int h){
    unsigned char *imgPixel = *imagePixel;
    unsigned char red,green,blue,alpha;
    long startByte = 0;
    
//    changePixelFun(imagePixel, w, h, changeRGBA_);

    unsigned char *imgPixelC = imgPixel;
    float redTemp,greenTemp,blueTemp,alphaTemp;
    long starttemp = 0;
    
    int offPixel[4][2] = { {-4, -4}, {-4, 4}, {4, -4}, {4,4} };
    int xtemp = 0, ytemp = 0;
   
    for(int y = 0;y < h; y++)
	{
		for (int x = 0; x < w; x++)
		{
            redTemp   = 0;
            greenTemp = 0;
			blueTemp  = 0;
            alphaTemp = 0;
            
            for (int off = 0; off < 4; off++) {
                ytemp = y + offPixel[off][0];
                if (ytemp >= h) {
                    ytemp = h-1;
                }else if(ytemp < 0){
                    ytemp = 0;
                }
                
                xtemp = x + offPixel[off][1];
                if (xtemp >= w) {
                    xtemp = w-1;
                }else if(xtemp < 0){
                    xtemp = 0;
                }
                
                starttemp = (ytemp * w + xtemp) * 4;
                
                redTemp   += imgPixel[starttemp];
                greenTemp += imgPixel[starttemp+1];
                blueTemp  += imgPixel[starttemp+2];
                alphaTemp += imgPixel[starttemp+3];
            }
            redTemp = redTemp/4;
			greenTemp = greenTemp/4;
			blueTemp = blueTemp/4;
            alphaTemp = alphaTemp/4;
            
            red = roundIntToUChar(redTemp);
            green = roundIntToUChar(greenTemp);
            blue = roundIntToUChar(blueTemp);
            alpha = roundIntToUChar(alphaTemp);
            
            
            startByte = (y * w + x) * 4;

            imgPixelC[startByte] = red;
            imgPixelC[startByte+1] = green;
            imgPixelC[startByte+2] = blue;
            imgPixelC[startByte+3] = alpha;
        }
    }
    
    imgPixel = imgPixelC;
    imagePixel = &imgPixelC;
    
}

void changePixel_Dipian(unsigned char **imagePixel, int w, int h){
    unsigned char *imgPixel = *imagePixel;
    unsigned char red,green,blue,alpha;
    long startByte = 0;
  
    
    float redTemp,greenTemp,blueTemp,alphaTemp;
    
    unsigned char *imgPixelC = imgPixel;
    
    for(int y = 0;y< h;y++)
	{
		for (int x = 0; x<w; x++)
		{
            
            startByte = (y * w + x) * 4;
            
            redTemp   = imgPixel[startByte];
            greenTemp = imgPixel[startByte+1];
            blueTemp  = imgPixel[startByte+2];
            alphaTemp = imgPixel[startByte+3];
            
            redTemp = 255 - redTemp;
			greenTemp = 255 - greenTemp;
			blueTemp = 255 - blueTemp;
            alphaTemp = alphaTemp;
            
            red = roundIntToUChar(redTemp);
            green = roundIntToUChar(greenTemp);
            blue = roundIntToUChar(blueTemp);
            alpha = roundIntToUChar(alphaTemp);
            
            imgPixelC[startByte] = red;
            imgPixelC[startByte+1] = green;
            imgPixelC[startByte+2] = blue;
            imgPixelC[startByte+3] = alpha;
        }
    }
    
    imgPixel = imgPixelC;
    imagePixel = &imgPixelC;
}




@end
